# Написание своего собственного метода map

Теперь, когда известно, как писать собственные функции высшего порядка, рассмотрим более реальный пример.

Представим, что у класса `List` нет метода `map`, и есть необходимость его написать. 
Первым шагом при создании функций является точное определение проблемы. 
Сосредоточившись только на `List[Int]`, получаем:

_Необходимо написать метод `map`, который можно использовать для применения функции к каждому элементу в `List[Int]`, 
возвращая преобразованные элементы в виде нового списка._

Учитывая это утверждение, начнем писать сигнатуру метода. 
Во-первых, известно, что функция должна приниматься в качестве параметра, 
и эта функция должна преобразовать `Int` в какой-то общий тип `A`, поэтому получаем:

```dotty
def map(f: (Int) => A)
```

Синтаксис использования универсального типа требует объявления этого символа типа перед списком параметров, 
поэтому добавляем объявление типа:

```dotty
def map[A](f: (Int) => A)
```

Далее известно, что `map` также должен принимать `List[Int]`:

```dotty
def map[A](f: (Int) => A, xs: List[Int])
```

Наконец, также известно, что `map` возвращает преобразованный список, содержащий элементы универсального типа `A`:

```dotty
def map[A](f: (Int) => A, xs: List[Int]): List[A] = ???
```

Теперь все, что нужно сделать, это написать тело метода. 
Метод `map` применяет заданную им функцию к каждому элементу в заданном списке 
для создания нового преобразованного списка. 
Один из способов сделать это - использовать выражение `for`:

```dotty
for x <- xs yield f(x)
```

`for` выражения зачастую делают код удивительно простым, и в данном случае - это все тело метода.

Объединив `for` с сигнатурой метода, получим автономный метод `map`, который работает с `List[Int]`:

```dotty
def map[A](f: (Int) => A, xs: List[Int]): List[A] =
  for x <- xs yield f(x)
```

##### Обобщим метод map

Обратим внимание, что выражение `for` не делает ничего, что зависит от типа `Int` внутри списка. 
Следовательно, можно заменить `Int` в сигнатуре типа параметром универсального типа `B`:

```dotty
def map[A, B](f: (B) => A, xs: List[B]): List[A] =
  for x <- xs yield f(x)
```

Получился метод `map`, который работает с любым списком. 

Демонстрация работы получившегося `map`:

```dotty
def double(i : Int) = i * 2
def strlen(s: String) = s.length

map(double, List(1, 2, 3))
// res0: List[Int] = List(2, 4, 6)
map(strlen, List("a", "bb", "ccc"))
// res1: List[Int] = List(1, 2, 3)
```

Теперь, когда рассмотрены методы, принимающие функции в качестве входных параметров, 
перейдем к методам, возвращающим функции.


---

**Ссылки:**

- [Scala3 book](https://docs.scala-lang.org/scala3/book/fun-write-map-function.html)
